<?php
/**
 * 用户令牌
 * xionggan
 * Date: 2019/09/11
 */

namespace service;

use think\Request;
use other\BaseRedis;
use think\facade\Env;
use think\facade\Config;
use lishaoen\JWT\JWT;

class UserTokenService
{

    protected $request  = null;
    protected $secret   = null;
    protected $expire   = null;
    protected $flush    = null;
    protected $error    = null;

    /**
     * 构造
     */
    public function __construct(Request $request = null)
    {
        $this->request  = $request;
        $this->secret   = Env::get('backend_jwt.secret') ?? 'secret';
        $this->expire   = Env::get('backend_jwt.expire') ?? '3600';
        $this->flush    = Env::get('backend_jwt.flush') ?? false;
    }

    /**
     * 获取错误
     * @return string
     */
    public function getError()
    {
        return $this->error;
    }

    /**
     * 生成用户token
     * @param  number $user_id      用户ID
     * @param  number $identity_id  用户身份ID
     * @return string               token
     */
    public function createToken($user_id, $identity_id='')
    {
        if (empty($user_id)) {
            $this->error = '生成令牌，缺少用户ID';
            return;
        }

        try {
            $time = time();
            $data = [
                'iss' => 'https://www.jiunidu.com',
                'aud' => 'https://admin.jiunidu.com',
                'iat' => $time,
                'exp' => $time + $this->expire,
                'uid' => $user_id,
                'ide' => $identity_id,
                'jti' => $this->getTokenId($user_id, $identity_id, $time),
            ];

            $token = JWT::encode($data, $this->secret);
            $redis = BaseRedis::getInstance();
            $redis->set('backend_jwt_'.$token, json_encode($data), $this->expire);

            return $token;
        } catch (\Exception $e) {
            $this->error = '令牌生成失败';
            return;
        }
    }

    /**
     * 查询token内容
     * @param  string   $token   令牌
     * @param  boole    $flush   是否刷新
     * @return mixed
     */
    public function checkToken($token, $flush=false)
    {
        try {
            $redis = BaseRedis::getInstance();
            $cache = $redis->get('backend_jwt_'.$token);
            
            if (empty($cache)) {
                $this->error = '令牌不存在';
                return;
            }

            if ($flush) {
                $redis->rm('backend_jwt_'.$token);
            }

            JWT::$leeway = 10;
            $token = (array) JWT::decode($token, $this->secret, array('HS256'));

            if (empty($token)) {
                $this->error = '令牌错误';
                return;
            }

            $token_id = $this->getTokenId($token['uid'], $token['ide'], $token['iat']);

            if ($token['jti'] != $token_id) {
                $this->error = '非法请求';
                return;
            }

            return $token;

        } catch (\Exception $e) {
            $this->error = '身份校验失败';
            return;
        }
    }

    /**
     * 刷新token有效期
     * @param  string   $token   token
     * @return array
     */
    public function flushToken($token)
    {
        try {
            $token = $this->checkToken($token, $this->flush);

            if (!empty($this->error)) {
                return;
            }

            $token = $this->createToken($token['uid'], $token['ide']);

            if (!empty($this->error)) {
                return;
            }

            return $token;
        } catch (\Exception $e) {
            $this->error = '令牌刷新失败';
            return;
        }
    }

    /**
     * token id 生成
     * @param  number   $user_id        用户id
     * @param  number   $identity_id    用户身份id
     * @param  number   $time           时间戳
     * @return string   token id
     */
    private function getTokenId($user_id, $identity_id, $time)
    {
        $ip = !is_null($this->request) ? $this->request->ip() : '';
        return md5(
            $user_id.$identity_id.$ip.$_SERVER['HTTP_USER_AGENT'].$this->secret.$time
        );
    }

}
